home *** CD-ROM | disk | FTP | other *** search
Text File | 1996-06-22 | 7.7 KB | 300 lines | [TEXT/MMCC] |
- /*============================================================
- Exceptions.c
-
- greggor@apple.com
-
- New API matches C++ exceptions as much as possible
-
- Usage:
-
- #include "Exceptions.h"
-
- Ptr ptr1 = nil;
- Ptr ptr2 = nil;
- OSErr err = noErr;
-
- NOREGISTER(ptr1);
- NOREGISTER(ptr2);
-
- Try
- {
- ptr1 = NewPtr( 1024 * 1024 );
- FailErr(MemError());
- ptr2 = NewPtr( 15 );
- FailErr(MemError());
-
- if(CatastrophicError())
- Throw(eCatastrophicError);
- }
- Catch(err)
- {
- //
- // Clean up here
- //
- if( ptr1 != nil )
- DisposPtr( ptr1 );
- if( ptr2 != nil )
- DisposPtr( ptr2 );
-
- if(err == eCatastrophicError)
- Alert(blah blah blah);
-
- //
- // Fail again...
- //
- Throw(err);
- }
-
- ============================================================*/
- #include <Types.h>
- #include <Memory.h>
- #include <Resources.h>
- #include <setjmp.h>
-
- #include "Exceptions.h"
-
- #define DEBUGMESSAGES 1
-
- // #define FAILMESSAGES
-
- TException* gExceptionStack = nil;
- NotifyFailureProc gNotifyProc = nil;
-
- //--------------------------------------------------------------------------------
- // TException::Setup
- //
- // This function is called by the TRY macro
- //--------------------------------------------------------------------------------
- Boolean TException::Setup()
- {
- Boolean firstTime = false;
-
- if(this->fMagicFailID == kMagicFailID)
- {
- if((this->fFlags & kExceptionThrown) == 0)
- {
- #ifdef DEBUGMESSAGES
- if((this->fFlags & kTryBlockHasBeenSetup) != 0)
- DebugStr("\pTry setup reentered for initialization in a bogus way");
- #endif
-
- //
- // Clear the 'is resignaling' bit of the
- // failure stack
- //
- if(gExceptionStack != nil)
- gExceptionStack->fFlags &= ~kResignalingFailure;
-
- //
- // Push the new entry onto the failure stack
- //
- this->fNext = gExceptionStack;
- gExceptionStack = this;
-
- this->fFlags |= (kTryFrameOnFailureStack | kTryBlockHasBeenSetup);
- firstTime = true;
- }
- else
- {
- #ifdef DEBUGMESSAGES
- if((this->fFlags & kTryBlockHasBeenSetup) == 0)
- DebugStr("\pTry setup reentered for cleanup in a bogus way");
- #endif
-
- this->TearDown();
- }
- }
- #ifdef DEBUGMESSAGES
- else
- DebugStr("\pCorrupt fail info in Setup");
- #endif
-
- return firstTime;
- } // TException::Setup
-
- //--------------------------------------------------------------------------------
- // TException::BeginTry
- //
- // This function is called by the TRY macro
- //--------------------------------------------------------------------------------
- Boolean TException::BeginTry()
- {
- Boolean firstTime = false;
-
- if(this->fMagicFailID == kMagicFailID)
- {
- if((this->fFlags & kTryInProgress) == 0)
- {
- this->fFlags |= kTryInProgress;
- firstTime = true;
- }
- else
- {
- this->fFlags |= kTrySucceeded;
- this->TearDown();
- }
- }
- #ifdef DEBUGMESSAGES
- else
- DebugStr("\pCorrupt fail info in Setup");
- #endif
-
- return firstTime;
- } // TException::BeginTry
-
- //--------------------------------------------------------------------------------
- // TException::TearDown
- //
- // 'TearDown' is called right before the exception handling block
- // is called (in which case fFlags & kExceptionThrown will be set), or after
- // a try block has completed successfully (in which case fFlags & kTrySucceeded
- // will be set).
- //--------------------------------------------------------------------------------
- void TException::TearDown()
- {
- if((gExceptionStack == this) && (this->fMagicFailID == kMagicFailID) && ((this->fFlags & kTryFrameOnFailureStack) != 0))
- {
- //
- // Pop an entry off the top of the exception stack
- //
- gExceptionStack = gExceptionStack->fNext;
- this->fFlags &= ~kTryFrameOnFailureStack;
-
- //
- // If we failed & there is anything left on the stack,
- // then note that we might want to resignal
- //
- if((gExceptionStack != nil) && ((this->fFlags & kExceptionThrown) != 0))
- {
- gExceptionStack->fFlags |= kResignalingFailure;
- gExceptionStack->fError = this->fError;
- }
- }
- #ifdef DEBUGMESSAGES
- else
- {
- if(gExceptionStack == nil)
- DebugStr( "\pError -- failure stack inexplicably nil" );
- else
- DebugStr( "\pTearDownTry: Failure handler stack corrupted" );
- }
- #endif
- } // TException::TearDown
-
- //--------------------------------------------------------------------------------
- // Throw
- //
- // Invoke exception handling
- //--------------------------------------------------------------------------------
- void Throw(OSErr theErr)
- {
- //
- // First do a little bit of sanity checking
- //
- if((gExceptionStack != nil) && (gExceptionStack->fMagicFailID == kMagicFailID))
- {
- //
- // Don't allow Throw(noErr)
- //
- if(theErr == noErr)
- theErr = eGeneralErr;
-
- //
- // Notify that we failed, unless we are resignalling
- // Note: If 'kResignalingFailure' is set, then fError
- // will be the error we set last time. Optionally, we
- // could require that 'theErr == gExceptionStack->fError'
- // in order to do notification, but we didn't.
- //
- if((gExceptionStack->fFlags & kResignalingFailure) == 0)
- NotifyFailure(theErr);
-
- //
- // Remember the error that caused the failure
- //
- gExceptionStack->fError = theErr;
-
- //
- // TRY does a setjmp; now we longjmp back to
- // that point with a longjmp. The 'TRY' macro
- // includes an 'if' statement that is true
- // the first time through and false when we longjmp
- // back to it; the EXCEPTion handler is just the
- // 'else' branch of that 'if' block.
- //
- // Note: This code used to use the result passed
- // back from longjmp, but that didn't work
- // under MetroWerks. My guess is that MetroWerks
- // was confused about the size of an int,
- // probably due to the libraries I chose to
- // link with. Rather than work out the problem,
- // I decided to stop using the return value
- // from longjmp.
- //
- gExceptionStack->fFlags |= kExceptionThrown;
- longjmp(gExceptionStack->fEnv, kExceptionThrown);
- }
- #ifdef DEBUGMESSAGES
- else
- {
- if(gExceptionStack == nil)
- DebugStr("\pthrow without try");
- else
- DebugStr( "\pthrow: Failure handler stack corrupted" );
- }
- #endif
- } // Throw
-
- //--------------------------------------------------------------------------------
- // SetExceptionNotifyProc
- //
- // Install a single routine called at failure time before
- // exception processing begins. This function is used in
- // conjunction with ShowStackChain to record and later show
- // the stack at the time of failure (left over from old 68K
- // code that doesn't work too well on PowerPC).
- //--------------------------------------------------------------------------------
- void SetExceptionNotifyProc(NotifyFailureProc notifyProc)
- {
- gNotifyProc = notifyProc;
- } // SetExceptionNotifyProc
-
- //--------------------------------------------------------------------------------
- // ExceptionNotifyProcInstalled
- //
- // This routine is used by ReportError so that it knows whether or
- // not it should show the 'sc6' button
- //--------------------------------------------------------------------------------
- Boolean ExceptionNotifyProcInstalled(void)
- {
- return gNotifyProc != nil;
- } // ExceptionNotifyProcInstalled
-
- //--------------------------------------------------------------------------------
- // NotifyFailure
- //
- // Notify that an error occured
- //--------------------------------------------------------------------------------
- void NotifyFailure(OSErr err)
- {
- #ifdef FAILMESSAGES
- DebugPrintf("Failure caused by an error %d", err);
- #endif
-
- // DebugStr("\pSomeone is about to fail...");
-
- if(gNotifyProc != nil)
- (*gNotifyProc)(err);
- } // NotifyFailure
-
- //--------------------------------------------------------------------------------
- // MakeVariableNoRegister
- //
- // This routine doesn't actually do anything; it's just used
- // by the NOREGISTER macro
- //--------------------------------------------------------------------------------
- void MakeVariableNoRegister( void* foo )
- {
-
- } // MakeVariableNoRegister
-